/* Payment Platform from Lucky Byte, Inc.
 *
 * Copyright (c) 2016 Lucky Byte, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lucky_byte.pay.tpdu;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * 多线程网络服务器
 */
public class PooledServer implements Runnable
{
	private static final Logger logger = LogManager.getLogger();

	private ServerSocket server_socket = null;
	private boolean is_stopped = false;
	private Properties properties;
	private ExecutorService thread_pool;

	public PooledServer(Properties properties) {
		if (properties == null) {
			throw new RuntimeException("未提供 TPDU 配置文件.");
		}
		this.properties = properties;
		this.thread_pool = Executors.newCachedThreadPool();
	}

	/**
	 * 允许运行时动态改变配置文件
	 */
	public void setProperties(Properties properties) {
		logger.info("新配置将在处理下个请求时生效(不影响已有连接).");
		this.properties = properties;
	}

	public void run() {
		try {
			int listen_port;
			try {
				String port = properties.getProperty("tpdu.listen_port");
				if (port == null) {
					logger.error("配置项[tpdu.listen_port]为空，启动服务器失败.");
					return;
				}
				listen_port = Integer.parseInt(port.trim());
			} catch (NumberFormatException e) {
				logger.error("配置项[tpdu.listen_port]值[{}]不是有效的数字，"
						+ "启动服务器失败.",
						properties.getProperty("tpdu.listen_port"));
				return;
			}
			this.server_socket = new ServerSocket(listen_port);
			logger.info("启动 BytePay TPDU 网控，绑定端口[{}]，进程[{}] ...",
					listen_port,
					ManagementFactory.getRuntimeMXBean().getName());
		} catch (IOException e) {
			logger.error("创建监听 Socket 错误[{}].", e.getMessage());
			return;
		}
		while (!this.is_stopped) {
			try {
				Socket accept_socket = this.server_socket.accept();
				int so_timeout;
				try {
					String timeout = properties.getProperty("tpdu.so_timeout");
					if (timeout != null) {
						so_timeout = Integer.parseInt(timeout.trim());
					} else {
						logger.warn("[tpdu.so_timeout]未配置，使用默认值 30.");
						so_timeout = 30;
					}
				} catch (NumberFormatException e) {
					logger.warn("配置项[tpdu.so_timeout]值[{}]无效，使用默认值 30.",
							properties.getProperty("tpdu.so_timeout"));
					so_timeout = 30;
				}
				accept_socket.setSoTimeout(so_timeout * 1000);

				InetAddress addr = accept_socket.getInetAddress();
				logger.info("接收客户端[{}][{}]的连接，开始处理...",
						addr.getHostAddress(), accept_socket.getPort());

				this.thread_pool.execute(
						new Worker(accept_socket, this.properties));
			} catch (IOException e) {
				if (this.is_stopped) {
					break;
				}
				logger.error("接收客户端连接错误[{}].", e.getMessage());
			}
		}
		this.thread_pool.shutdown();
		logger.info("BytePay TPDU 网控停止运行.");
	}

	/**
	 * 停止服务器
	 */
	public synchronized void stop() {
		if (!this.is_stopped) {
			this.is_stopped = true;
			try {
				this.server_socket.close();
			} catch (IOException e) {
				logger.catching(e);
			}
		}
	}

}
